import config from './config'
import db from './database'
import temp from './temp'

import characterObj from './obj/character'
import bagObj from './obj/bag'
import enemyObj from './obj/enemy'

export default {
  vm: {},
  data: {},
  tempData: {},
  loopClock: 0,
  tickTime: 0,
  tickCount: 0,
  renderCount: 0,
  initTime: 0,
  lastLoopTime: 0,
  lastSaveTime: 0,
  currentTime: 0,
  logData: [],
  logCount: 0,

  init(that) {
    this.initTime = this.lastLoopTime = this.lastSaveTime = new Date().getTime()

    this.tickTime = 1000 / config.fps

    this.vm = that

    db.init().then((response) => {
      this.data = response
      this.tempData = JSON.parse(JSON.stringify(temp))

      this.createObj()

      this.loopClock = setTimeout(() => { this.loop() }, this.tickTime)
    })

    // db.fullReset()
  },

  createObj() {
    this.tempData.character = new characterObj(this.data.state.character.attribute)
    this.tempData.bag = new bagObj(this.data.state.bag)
  },

  // worker() {
  //   let blob = new Blob([`
  //   onmessage = (e) => {
  //     setInterval(() => {
  //       postMessage('tick')
  //     }, ` + this.tickTime + `)
  //   }
  //   `])
  //   let blobURL = URL.createObjectURL(blob)
  //   let tickWorker = new Worker(blobURL)
  //   tickWorker.postMessage('tick')
  //   tickWorker.onmessage = (e) => { this.log('测试', this.tickCount) }
  // },

  loop() {
    this.currentTime = new Date().getTime()

    let timedelay = (this.currentTime - this.lastLoopTime) - (this.tickTime)

    if (timedelay > config.exceedDelay) {
      timedelay = 0
    }

    else {
      while (timedelay > (this.tickTime / 2)) {
        this.tick()
        timedelay -= this.tickTime
      }

      this.tick()

      this.render()

      this.lastLoopTime = this.currentTime - timedelay

      this.autoSave()
    }

    setTimeout(() => { this.loop() }, this.tickTime - timedelay)
  },

  tick() {
    if (this.tempData.state.fight.change !== this.tempData.state.fight.current) {
      this.tempData.state.fight.current = this.tempData.state.fight.change
      if (this.tempData.state.fight.current) {
        this.tempData.character.updateFightNextActionTick(this.tickCount)
        this.tempData.event.fight.enemy.obj = new enemyObj(this.tempData.event.fight.enemy.data)
        this.tempData.event.fight.enemy.obj.updateNextActionTick(this.tickCount)
      }
      else {
        this.tempData.character.updateIdleNextActionTick(this.tickCount)
      }
    }

    if (this.tempData.state.search.change !== this.tempData.state.search.current) {
      this.tempData.state.search.current = this.tempData.state.search.change
      if (this.tempData.state.search.current) {
        this.tempData.character.updateSearchNextActionTick(this.tickCount)
      }
    }

    this.tempData.state.recovery.current = this.tempData.state.recovery.change

    if (this.tempData.state.fight.change) {
      this.fight()
    }
    else {
      this.idle()
    }

    if (this.tempData.state.search.change) {
      this.search()
    }

    if (this.tempData.state.recovery.change) {
      this.recovery()
    }

    this.tickCount++
  },

  render() {
    this.vm.$store.commit('render')
    this.renderCount++
  },

  save() {
    return new Promise((resolve, reject) => {
      db.save(this.data).then(() => {
        resolve({})
      })
    })
  },

  autoSave() {
    if ((this.currentTime - this.lastSaveTime) > config.saveInterval) {
      db.save(this.data).then((response) => { })
      this.lastSaveTime = this.currentTime
    }
  },

  reset() {
    return new Promise((resolve, reject) => {
      db.reset().then((response) => {
        this.logData = []
        response.state.scene = 'home'
        this.data = response
        this.tempData = JSON.parse(JSON.stringify(temp))
        this.createObj()
        this.vm.$store.commit('update')
        this.save().then(() => {
          resolve({})
        })
      })
    })
  },

  inputSave(input) {
    return new Promise((resolve, reject) => {
      let saveObj = JSON.parse(decodeURIComponent(atob(input)))
      db.save(saveObj).then(() => {
        this.logData = []
        this.data = saveObj
        this.tempData = JSON.parse(JSON.stringify(temp))
        this.createObj()
        this.vm.$store.commit('update')
        resolve({})
      })
    })
  },

  outputSave() {
    return new Promise((resolve, reject) => {
      db.save(this.data).then((response) => {
        resolve(response)
      })
    })
  },

  log(type, content) {
    this.logCount = this.logCount + 1

    if (this.logData.length >= 100) {
      this.logData.shift()
    }

    this.logData.push({
      id: this.logCount,
      content: '[ ' + type + ' ] ' + content
    })
  },

  idle() {
    if (this.tempData.character.attribute.hp.current >= this.tempData.character.attribute.hp.max || this.tempData.character.idle.nextActionTick !== this.tickCount) {
      return
    }

    let hpRecovery = Math.round(this.tempData.character.attribute.hp.max * this.tempData.character.attribute.hp.recoverySpeed)

    if (this.tempData.character.attribute.hp.current + hpRecovery > this.tempData.character.attribute.hp.max) {
      this.tempData.character.attribute.hp.current = this.tempData.character.attribute.hp.max
    }
    else {
      this.tempData.character.attribute.hp.current += hpRecovery
    }

    this.tempData.character.updateIdleNextActionTick(this.tickCount)
  },

  fight() {
    if (this.tempData.character.fight.nextActionTick === this.tickCount) {
      let reduceHp = this.tempData.character.attribute.atk - this.tempData.event.fight.enemy.obj.attribute.def
      if (reduceHp <= 0) {
        reduceHp = 0
      }

      this.tempData.event.fight.enemy.obj.reduceAttributeHp(reduceHp)
      this.tempData.character.updateFightNextActionTick(this.tickCount)
      this.log('战斗动态', '你使用普通攻击对 <em>' + this.tempData.event.fight.enemy.obj.attribute.name + '</em> 造成了 <em>' + reduceHp + '</em> 点伤害')
    }

    if (!this.tempData.event.fight.enemy.obj.alive) {
      let addExp = this.tempData.event.fight.enemy.obj.attribute.exp
      let addGold = this.tempData.event.fight.enemy.obj.attribute.gold

      addExp = this.tempData.character.addAttributeExp(addExp)
      this.tempData.bag.addContentGold(addGold)

      if (addExp > 0) {
        this.log('战斗结果', '战斗胜利，你得到了 <em>' + addExp + '</em> 经验，<em>' + addGold + '</em> 金币')
      }
      else {
        this.log('战斗结果', '战斗胜利，你得到了 <em>' + addGold + '</em> 金币')
      }

      this.tempData.state.fight.change = false

      if (!this.tempData.event.complete) {
        this.data.state.event.lastComplete = true
        this.tempData.updateFlag.event.complete = this.tempData.updateFlag.event.complete + 1

        if (this.data.state.map.position.max === 20) {
          this.log('游戏消息', '<em>恭喜！你已完成了游戏，你获得了成就「第一批玩家」！</em>')
        }
      }

      else {
        if (this.data.setting.event.fight.ready.autoContinueSearch) {
          this.tempData.state.search.change = true
        }
      }

      return
    }

    if (this.tempData.event.fight.enemy.obj.nextActionTick === this.tickCount) {
      let reduceHp = this.tempData.event.fight.enemy.obj.attribute.atk
      if (reduceHp <= 0) {
        reduceHp = 0
      }

      this.tempData.character.reduceAttributeHp(reduceHp)
      this.tempData.event.fight.enemy.obj.updateNextActionTick(this.tickCount)
      this.log('战斗动态', '<em>' + this.tempData.event.fight.enemy.obj.attribute.name + '</em> 使用普通攻击对你造成了 <em>' + reduceHp + '</em> 点伤害')
    }

    if (!this.tempData.character.alive) {
      let reduceExp = Math.floor(this.tempData.character.attribute.exp.max * 0.1)
      reduceExp = this.tempData.character.reduceAttributeExp(reduceExp)

      if (reduceExp > 0) {
        this.log('战斗结果', '战斗失败，你损失了 <em>' + reduceExp + '</em> 点经验')
      }
      else {
        this.log('战斗结果', '战斗失败')
      }

      this.tempData.state.fight.change = false
      this.tempData.character.alive = true
      return
    }
  },

  search() {
    if (this.tempData.character.search.nextActionTick === this.tickCount + 1) {
      this.tempData.state.search.change = false
      if (this.tempData.character.attribute.hp.current < this.tempData.character.attribute.hp.max) {
        this.tempData.state.recovery.change = true
      }
      else {
        this.tempData.state.fight.change = true
      }
    }
  },

  recovery() {
    if (this.tempData.character.attribute.hp.current >= this.tempData.character.attribute.hp.max) {
      this.tempData.state.recovery.change = false
      this.tempData.state.fight.change = true
    }
  }
}